package org.zoomdev.zoom.asm;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;

import java.util.Arrays;

public class Instructions {

    // 基本指令

    public static Instruction loadThis() {
        return GeneratorAdapter::loadThis;
    }

    public static Instruction loadArg(int index) {
        return mg -> mg.loadArg(index);
    }

    public static Instruction loadLocal(int index) {
        return mg -> mg.loadLocal(index);
    }

    public static Instruction storeLocal(int index) {
        return mg -> mg.storeLocal(index);
    }

    public static Instruction returnValue() {
        return GeneratorAdapter::returnValue;
    }

    public static Instruction dup() {
        return GeneratorAdapter::dup;
    }

    public static Instruction pop() {
        return GeneratorAdapter::pop;
    }

    public static Instruction swap() {
        return GeneratorAdapter::swap;
    }

    // 类型转换

    public static Instruction cast(Class<?> from, Class<?> to) {
        return mg -> {
            Type fromType = Type.getType(from);
            Type toType = Type.getType(to);

            // 相同类型不需要转换
            if (fromType.equals(toType)) return;

            // 基本类型之间的转换
            if (fromType.getSort() <= Type.DOUBLE && toType.getSort() <= Type.DOUBLE) {
                handlePrimitiveCast(mg, fromType, toType);
                return;
            }

            // 基本类型到包装类型 (自动装箱)
            if (isPrimitive(from) && isWrapper(to)) {
                handleBoxing(mg, from, to);
                return;
            }

            // 包装类型到基本类型 (自动拆箱)
            if (isWrapper(from) && isPrimitive(to)) {
                handleUnboxing(mg, from, to);
                return;
            }

            // 引用类型之间的转换
            mg.checkCast(toType);
        };
    }

    public static Instruction checkCast(Class<?> type) {
        return mg -> mg.checkCast(Type.getType(type));
    }

    public static Instruction instanceOf(Class<?> type) {
        return mg -> mg.instanceOf(Type.getType(type));
    }

    public static Instruction invokeVirtual(Class<?> owner, String methodName,
                                            Class<?> returnType, Class<?>... paramTypes) {
        return mg -> mg.invokeVirtual(
                Type.getType(owner),
                new Method(methodName, Type.getType(returnType),
                        Arrays.stream(paramTypes).map(Type::getType).toArray(Type[]::new))
        );
    }

    public static Instruction invokeInterface(Class<?> owner, String methodName,
                                              Class<?> returnType, Class<?>... paramTypes) {
        return mg -> mg.invokeInterface(
                Type.getType(owner),
                new Method(methodName, Type.getType(returnType),
                        Arrays.stream(paramTypes).map(Type::getType).toArray(Type[]::new))
        );
    }

    public static Instruction invokeStatic(Class<?> owner, String methodName,
                                           Class<?> returnType, Class<?>... paramTypes) {
        return mg -> mg.invokeStatic(
                Type.getType(owner),
                new Method(methodName, Type.getType(returnType),
                        Arrays.stream(paramTypes).map(Type::getType).toArray(Type[]::new))
        );
    }

    public static Instruction invokeConstructor(Class<?> owner, Class<?>... paramTypes) {
        return mg -> mg.invokeConstructor(
                Type.getType(owner),
                new Method("<init>", Type.VOID_TYPE,
                        Arrays.stream(paramTypes).map(Type::getType).toArray(Type[]::new))
        );
    }

    public static Instruction getField(String className, String fieldName, Class<?> fieldType) {
        // 验证输入参数
        if (className == null || className.isEmpty()) {
            throw new IllegalArgumentException("ClassName cannot be null or empty");
        }
        if (fieldName == null || fieldName.isEmpty()) {
            throw new IllegalArgumentException("Field name cannot be null or empty");
        }
        if (fieldType == null) {
            throw new IllegalArgumentException("Field type cannot be null");
        }

        // 将类名转换为内部名称格式（如果包含点号）
        String internalName = className.contains(".")
                ? className.replace('.', '/')
                : className;

        // 获取字段类型描述符
        String descriptor = Type.getType(fieldType).getDescriptor();

        return mg -> {
            // 生成 GETFIELD 指令
            mg.visitFieldInsn(
                    Opcodes.GETFIELD,   // 操作码
                    internalName,       // 类内部名称
                    fieldName,          // 字段名
                    descriptor          // 字段类型描述符
            );
        };
    }

    public static Instruction getField(Class<?> owner, String fieldName, Class<?> fieldType) {
        return mg -> mg.getField(Type.getType(owner), fieldName, Type.getType(fieldType));
    }

    public static Instruction getField(Type type, String fieldName, Type fieldType) {
        return mg -> mg.getField(type, fieldName, fieldType);
    }

    // 方法调用

    public static Instruction putField(Class<?> owner, String fieldName, Class<?> fieldType) {
        return mg -> mg.putField(Type.getType(owner), fieldName, Type.getType(fieldType));
    }

    public static Instruction putField(Type owner, String fieldName, Type fieldType) {
        return mg -> mg.putField(owner, fieldName, fieldType);
    }

    public static Instruction putField(String ownerInternalName, String fieldName, Class<?> fieldType) {
        return mg -> {
            // 将内部名称转换为 Type 对象
            Type ownerType = Type.getObjectType(ownerInternalName);

            // 获取字段类型的 Type 对象
            Type fieldTypeObj = Type.getType(fieldType);

            // 调用 putField 指令
            mg.putField(ownerType, fieldName, fieldTypeObj);
        };
    }

    public static Instruction getStatic(Class<?> owner, String fieldName, Class<?> fieldType) {
        return mg -> mg.getStatic(Type.getType(owner), fieldName, Type.getType(fieldType));
    }

    public static Instruction putStatic(Class<?> owner, String fieldName, Class<?> fieldType) {
        return mg -> mg.putStatic(Type.getType(owner), fieldName, Type.getType(fieldType));
    }

    public static Instruction newInstance(Class<?> type) {
        return mg -> mg.newInstance(Type.getType(type));
    }

    // 字段访问

    public static Instruction newArray(Class<?> componentType) {
        return mg -> mg.newArray(Type.getType(componentType));
    }

    public static Instruction push(Object value) {
        return mg -> {
            if (value instanceof Integer) {
                mg.push((Integer) value);
            } else if (value instanceof String) {
                mg.push((String) value);
            } else if (value instanceof Boolean) {
                mg.push((Boolean) value);
            } else if (value instanceof Long) {
                mg.push((Long) value);
            } else if (value instanceof Float) {
                mg.push((Float) value);
            } else if (value instanceof Double) {
                mg.push((Double) value);
            } else if (value instanceof Character) {
                mg.push((Character) value);
            } else if (value instanceof Byte) {
                mg.push((Byte) value);
            } else if (value instanceof Short) {
                mg.push((Short) value);
            } else {
                throw new IllegalArgumentException("Unsupported push type: " + value.getClass());
            }
        };
    }

    public static Instruction pushNull() {
        return mg -> mg.visitInsn(Opcodes.ACONST_NULL);
    }

    public static Instruction arrayLoad(Class<?> componentType) {
        if (componentType == byte.class || componentType == boolean.class) {
            return mg -> mg.visitInsn(Opcodes.BALOAD);
        } else if (componentType == char.class) {
            return mg -> mg.visitInsn(Opcodes.CALOAD);
        } else if (componentType == short.class) {
            return mg -> mg.visitInsn(Opcodes.SALOAD);
        } else if (componentType == int.class) {
            return mg -> mg.visitInsn(Opcodes.IALOAD);
        } else if (componentType == float.class) {
            return mg -> mg.visitInsn(Opcodes.FALOAD);
        } else if (componentType == long.class) {
            return mg -> mg.visitInsn(Opcodes.LALOAD);
        } else if (componentType == double.class) {
            return mg -> mg.visitInsn(Opcodes.DALOAD);
        } else { // 对象类型
            return mg -> mg.visitInsn(Opcodes.AALOAD);
        }
    }

    // 对象创建

    public static Instruction arrayStore(Class<?> componentType) {
        return mg -> mg.arrayStore(Type.getType(componentType));
    }

    public static Instruction aaload() {
        return mg -> mg.visitInsn(Opcodes.AALOAD);
    }

    // 常量加载

    public static Instruction aastore() {
        return mg -> mg.visitInsn(Opcodes.AASTORE);
    }

    public static Instruction goTo(org.objectweb.asm.Label label) {
        return mg -> mg.goTo(label);
    }

    // 数组操作

    public static Instruction ifCmp(Class<?> type, int mode, org.objectweb.asm.Label label) {
        return mg -> mg.ifCmp(Type.getType(type), mode, label);
    }

    public static Instruction ifNull(org.objectweb.asm.Label label) {
        return mg -> mg.ifNull(label);
    }

    public static Instruction ifNonNull(org.objectweb.asm.Label label) {
        return mg -> mg.ifNonNull(label);
    }

    public static Instruction math(int op, Class<?> type) {
        return mg -> mg.math(op, Type.getType(type));
    }

    // 控制流

    public static Instruction custom(InstructionCustomizer customizer) {
        return mg -> customizer.customize(mg);
    }

    public static Instruction sequence(Instruction... instructions) {
        return mg -> {
            for (Instruction instruction : instructions) {
                instruction.apply(mg);
            }
        };
    }

    private static void handlePrimitiveCast(GeneratorAdapter mg, Type fromType, Type toType) {
        // 数字类型转换
        if (fromType.getSort() == Type.INT) {
            switch (toType.getSort()) {
                case Type.LONG:
                    mg.visitInsn(Opcodes.I2L);
                    break;
                case Type.FLOAT:
                    mg.visitInsn(Opcodes.I2F);
                    break;
                case Type.DOUBLE:
                    mg.visitInsn(Opcodes.I2D);
                    break;
                case Type.BYTE:
                    mg.visitInsn(Opcodes.I2B);
                    break;
                case Type.CHAR:
                    mg.visitInsn(Opcodes.I2C);
                    break;
                case Type.SHORT:
                    mg.visitInsn(Opcodes.I2S);
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported cast from int to " + toType);
            }
            return;
        }

        if (fromType.getSort() == Type.LONG) {
            switch (toType.getSort()) {
                case Type.INT:
                    mg.visitInsn(Opcodes.L2I);
                    break;
                case Type.FLOAT:
                    mg.visitInsn(Opcodes.L2F);
                    break;
                case Type.DOUBLE:
                    mg.visitInsn(Opcodes.L2D);
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported cast from long to " + toType);
            }
            return;
        }

        if (fromType.getSort() == Type.FLOAT) {
            switch (toType.getSort()) {
                case Type.INT:
                    mg.visitInsn(Opcodes.F2I);
                    break;
                case Type.LONG:
                    mg.visitInsn(Opcodes.F2L);
                    break;
                case Type.DOUBLE:
                    mg.visitInsn(Opcodes.F2D);
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported cast from float to " + toType);
            }
            return;
        }

        if (fromType.getSort() == Type.DOUBLE) {
            switch (toType.getSort()) {
                case Type.INT:
                    mg.visitInsn(Opcodes.D2I);
                    break;
                case Type.LONG:
                    mg.visitInsn(Opcodes.D2L);
                    break;
                case Type.FLOAT:
                    mg.visitInsn(Opcodes.D2F);
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported cast from double to " + toType);
            }
        }
    }

    private static void handleBoxing(GeneratorAdapter mg, Class<?> primitive, Class<?> wrapper) {
        // 特殊处理 void
        if (primitive == void.class) {
            mg.visitInsn(Opcodes.ACONST_NULL);
            return;
        }

        // 获取包装类型的 valueOf 方法
        String methodName = "valueOf";
        String methodDesc = "(" + Type.getDescriptor(primitive) + ")L" + Type.getInternalName(wrapper) + ";";

        mg.invokeStatic(
                Type.getType(wrapper),
                new Method(methodName, Type.getType(wrapper), new Type[]{Type.getType(primitive)})
        );
    }

    // 数学运算

    private static void handleUnboxing(GeneratorAdapter mg, Class<?> wrapper, Class<?> primitive) {
        // 特殊处理 void
        if (primitive == void.class) {
            mg.visitInsn(Opcodes.POP);
            return;
        }

        // 获取拆箱方法名
        String methodName;
        if (primitive == boolean.class) methodName = "booleanValue";
        else if (primitive == byte.class) methodName = "byteValue";
        else if (primitive == char.class) methodName = "charValue";
        else if (primitive == short.class) methodName = "shortValue";
        else if (primitive == int.class) methodName = "intValue";
        else if (primitive == long.class) methodName = "longValue";
        else if (primitive == float.class) methodName = "floatValue";
        else if (primitive == double.class) methodName = "doubleValue";
        else throw new IllegalArgumentException("Unsupported primitive type: " + primitive);

        String methodDesc = "()" + Type.getDescriptor(primitive);

        mg.invokeVirtual(
                Type.getType(wrapper),
                new Method(methodName, Type.getType(primitive), new Type[0])
        );
    }

    // 自定义指令

    // 检查是否是基本类型
    private static boolean isPrimitive(Class<?> cls) {
        return cls.isPrimitive() || cls == Void.class;
    }

    // 检查是否是包装类型
    private static boolean isWrapper(Class<?> cls) {
        return cls == Boolean.class ||
                cls == Byte.class ||
                cls == Character.class ||
                cls == Short.class ||
                cls == Integer.class ||
                cls == Long.class ||
                cls == Float.class ||
                cls == Double.class ||
                cls == Void.class;
    }

    // 接口用于自定义指令
    public interface InstructionCustomizer {
        void customize(GeneratorAdapter mg);
    }
}